package se.webbzon.oschi01.worldobject.appearance;

import java.util.List;

import javax.media.j3d.BranchGroup;
import javax.media.j3d.Group;
import javax.media.j3d.Node;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;

import se.webbzon.oschi01.j3d.AutoOrderedGroup;

public class GroupAppearance3D implements Appearance3D {
	
	// Group appearance properties
	//public static final int EXTENDING 		= (1<<0);
	//public static final int CURTAILING		= (1<<1);
	public static final int TRANSPARENCY	= (1<<0);
	
	// The branch group associated with this java3d instance
	private final BranchGroup branchGroup;
		
	// The rendering order number associated with this java3d instance
	private final int renderingOrder;
		
	// The animation of this appearance
	private Animation3D animation;
	
	// A switch used to turn visibility on and off
	//private final Switch switchGroup;
	
	// The group with all child nodes
	//private final Group childGroup;
	
	// The properties of this group appearance
	private final int properties;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new group appearance from a list of appearances. 
	 * The rendering order of this group will be set to zero. **/
	public GroupAppearance3D(List<Appearance3D> appearances) {
		this(0,appearances);
	}
	
	/** Creates a new group appearance from a list of appearances. 
	 * The rendering order of this group is given by the input. **/
	public GroupAppearance3D(	int renderingOrder,
								List<Appearance3D> appearances) {
		this(renderingOrder,0,appearances);
	}
	
	/** Creates a new group appearance from a list of appearances with
	 * a given set of properties. The rendering order of this group is 
	 * given by the input. **/
	public GroupAppearance3D(	int renderingOrder,
								int groupAppearanceProperties,
								List<Appearance3D> appearances) {
		this.renderingOrder = renderingOrder;
		properties = groupAppearanceProperties;
		branchGroup = new BranchGroup();
		branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
		Switch switchGroup = new Switch();
		switchGroup.setCapability(Switch.ALLOW_SWITCH_READ);
		switchGroup.setCapability(Switch.ALLOW_SWITCH_WRITE);
		switchGroup.setWhichChild(Switch.CHILD_ALL);
		TransformGroup transformGroup = new TransformGroup();
		transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		
		final boolean 	transparency = isSet(properties,TRANSPARENCY);
						//curtailing = isSet(properties,CURTAILING);
		
		// Create auto-ordered group
		AutoOrderedGroup aog;
		if (transparency)
			aog = new AutoOrderedGroup(); 
		else
			aog = null;
		
		// Add appearances to child group
		if (appearances != null) {
			for (Appearance3D appearance : appearances) {
				BranchGroup child = appearance.getJ3dBranch();

				if (child != null) {	
					// Add node to child group
					if (transparency)
						aog.addChild(child, appearance.getRenderingOrder());
					else
						transformGroup.addChild(child);
				}
			}
		}
		
		// Attach to structure
		if (transparency)
			transformGroup.addChild(aog);
		switchGroup.addChild(transformGroup);
		branchGroup.addChild(switchGroup);
	}
	
	/*============================================================
	Public Methods
	============================================================*/

	/** Adds an appearance to the appearance group. **/
	public boolean addAppearance(Appearance3D appearance) {
		//if (isSet(properties,EXTENDING)) { 
			// Retrieve the node of the appearance
			BranchGroup child = appearance.getJ3dBranch();
			if (child == null)
				return true;
			
			Group g = (Group) branchGroup.getChild(0);
			TransformGroup tg = (TransformGroup) g.getChild(0);
			
			// Create a new branch group for the node
			//BranchGroup branchGroup = new BranchGroup();
			//branchGroup.setCapability(BranchGroup.ALLOW_DETACH);
			
			// Add the node the group
			//branchGroup.addChild(childNode);
			if (isSet(properties,TRANSPARENCY))
				((AutoOrderedGroup) tg.getChild(0)).
					addChild(child,appearance.getRenderingOrder());
			else
				tg.addChild(child);
			return true;
		//}
		//return false;
	}
	
	/** Removes an appearance from the appearance group. **/
	public boolean removeAppearance(Appearance3D appearance) {
		//if (isSet(properties,CURTAILING)) {
			// Retrieve the node of the appearance
			BranchGroup child = appearance.getJ3dBranch();
			if (child == null)
				return true;
			
			Group g = (Group) branchGroup.getChild(0);
			TransformGroup tg = (TransformGroup) g.getChild(0);
			
			// Remove the node and the branch group from the group
			/*BranchGroup branchGroup = (BranchGroup) childNode.getParent();
			if (branchGroup.getParent() == childGroup) {
				childGroup.removeChild(branchGroup);
				branchGroup.removeChild(childNode);
				return true;
			}*/
			if (isSet(properties,TRANSPARENCY))
				((Group) tg.getChild(0)).removeChild(child);
			else
				tg.removeChild(child);
			return true;
		//}
		//return false;
	}
	
	@Override public boolean getVisible() {
		Switch switchGroup = (Switch) branchGroup.getChild(0);
		return (switchGroup.getWhichChild() == Switch.CHILD_ALL);
	}

	@Override public void setVisible(boolean visible) {
		Switch switchGroup = (Switch) branchGroup.getChild(0);
		if (visible)
			switchGroup.setWhichChild(Switch.CHILD_ALL);
		else
			switchGroup.setWhichChild(Switch.CHILD_NONE);
	}
	
	@Override public void onStepEnd() {
		if (animation != null)
			animation.onStepEnd(this);
	}

	@Override public void setAnimation(Animation3D animation) {
		this.animation = animation;
	}

	@Override public Animation3D getAnimation() {
		return animation;
	}

	@Override public void getTransform(Transform3D transform) {
		Group g = (Group) branchGroup.getChild(0);
		TransformGroup tg = (TransformGroup) g.getChild(0);
		tg.getTransform(transform);
	}

	@Override public void setTransform(Transform3D transform) {
		Group g = (Group) branchGroup.getChild(0);
		TransformGroup tg = (TransformGroup) g.getChild(0);
		tg.setTransform(transform);
	}

	@Override public int getRenderingOrder() {
		return renderingOrder;
	}

	@Override public BranchGroup getJ3dBranch() {
		return branchGroup;
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Returns true if the given property is set in the given properties. **/
	private static final boolean isSet(int properties, int property) {
		return ((properties & property) == property);
	}
	
}
